using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using SceneEngine;

namespace SceneEditor
{
    public class MayaStyleCamera : ICamera
    {
        private Matrix rotation;
        private Vector3 position;

        private Vector3 forward;

        private Vector3 moveVector;
        private Vector3 pivotPosition;

        private Matrix view;
        private Matrix projection;

        private float zoom = 30.0f;

        private float speed = 10.0f;

        private float horizontalAngle = MathHelper.PiOver2;
        private float verticalAngle = 1.14f;

        private const float verticalAngleMin = 0.01f;
        private const float verticalAngleMax = MathHelper.Pi - 0.01f;

        private MouseState prevMouseState;

        public float Zoom
        {
            get { return zoom; }
            set 
            { 
                zoom = value;

                if (zoom < 10)
                {
                    zoom = 10;
                    pivotPosition += forward * zoom;
                }
            }
        }

        public float HorizontalAngle
        {
            get { return horizontalAngle; }
            set { horizontalAngle = value; }
        }
        public float Yaw
        {
            get { return verticalAngle; }
            set
            {
                verticalAngle = MathHelper.Clamp(value, verticalAngleMin, verticalAngleMax);
            }
        }

        public Matrix View
        {
            get { return view; }
        }
        public Matrix Projection
        {
            get { return projection; }
        }

        public Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }
        public Vector3 Forward
        {
            get { return forward; }
        }

        public float Speed
        {
            get { return speed; }
            set { speed = value; }
        }

        public BoundingFrustum Frustum
        {
            get { return new BoundingFrustum(View * Projection); }
        }

        public float NearPlane
        {
            get { return 1.0f; }
        }
        public float FarPlane
        {
            get { return 10000.0f; }
        }

        public MayaStyleCamera(Vector3 position, float speed)
        {
            this.position = position;
            this.speed = speed;

            rotation = Matrix.CreateRotationX(verticalAngle) *
                Matrix.CreateRotationY(horizontalAngle);

            Vector3 forward = Vector3.Transform(Vector3.Forward, rotation);
            pivotPosition = position + (forward * zoom);

            float aspectRatio = EngineGlobals.GraphicsDevice.Viewport.AspectRatio;

            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, 1.0f, 10000.0f);
        }
        public MayaStyleCamera(Vector3 position, Vector3 forward, float speed)
        {
            this.position = position;
            this.speed = speed;

            pivotPosition = position + (forward * zoom);

            float aspectRatio = EngineGlobals.GraphicsDevice.Viewport.AspectRatio;

            projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, aspectRatio, 1.0f, 10000.0f);
        }

        public void Update(float gameTime)
        {
            KeyboardState keyState = Keyboard.GetState();
            MouseState mouseState = Mouse.GetState();

            float deltaX = mouseState.X - prevMouseState.X;
            float deltaY = mouseState.Y - prevMouseState.Y;

            moveVector = Vector3.Zero;

            if (mouseState.LeftButton == ButtonState.Pressed && keyState.IsKeyDown(Keys.LeftAlt))
            {
                Yaw -= deltaY * gameTime;
                HorizontalAngle -= deltaX * gameTime;
            }

            else if (mouseState.MiddleButton == ButtonState.Pressed && keyState.IsKeyDown(Keys.LeftAlt))
            {
                moveVector.X -= deltaX * speed * gameTime;
                moveVector.Y += deltaY * speed * gameTime;
            }

            else if (mouseState.RightButton == ButtonState.Pressed && keyState.IsKeyDown(Keys.LeftAlt))
            {
                if (deltaX == 0 && deltaY != 0)
                    Zoom += deltaY * speed * gameTime;
                if (deltaY == 0 && deltaX != 0)
                    Zoom -= deltaX * speed * gameTime;
                else
                    Zoom += (deltaY - deltaX) * speed * gameTime;
            }

            Vector3 cameraPosition = new Vector3(0, zoom, 0);
            
            rotation = Matrix.CreateTranslation(cameraPosition) * Matrix.CreateRotationX(verticalAngle) * 
                Matrix.CreateRotationY(horizontalAngle);

            cameraPosition = rotation.Translation;

            Matrix tempRot = Matrix.CreateRotationY(horizontalAngle);
            Vector3.Transform(ref moveVector, ref tempRot, out moveVector);

            pivotPosition += moveVector;
            cameraPosition += pivotPosition;

            position = cameraPosition;

            forward = Vector3.Normalize(pivotPosition - position);

            view = Matrix.CreateLookAt(this.position, pivotPosition, Vector3.Up);

            prevMouseState = mouseState;
        }

        public void ChangeDirection(Vector3 targetPos)
        {
            pivotPosition = targetPos;
        }

        public void UpdateProjection()
        {
            float aspectRatio = EngineGlobals.GraphicsDevice.Viewport.AspectRatio;

            projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.PiOver4, aspectRatio, 1.0f, 10000.0f);
        }
    }
}
